dojo.provide("demo.data.CustomQueryStore");

dojo.require("dojox.data.QueryReadStore");

dojo.declare("demo.data.CustomQueryStore", dojox.data.QueryReadStore, {
    //  summary:
    //      This class provides a store that is mainly intended to be used
    //      for loading data dynamically from the server, used i.e. for
    //      retrieving chunks of data from huge data stores on the server (by server-side filtering!).
    //      Upon calling the fetch() method of this store the data are requested from
    //      the server if they are not yet loaded for paging (or cached).
    //
    //      For example used for a combobox which works on lots of data. It
    //      can be used to retrieve the data partially upon entering the
    //      letters "ac" it returns only items like "action", "acting", etc.
    //
    // note:
    //      The field name "id" in a query is reserved for looking up data
    //      by id. This is necessary as before the first fetch, the store
    //      has no way of knowing which field the server will declare as
    //      identifier.

    /* @Override */
    isItemLoaded: function(/* anything */ something) {
        // Currently we have item["children"] as a state that tells if an item is loaded or not
        // if item["children"] === true, means the item is not loaded.
        var isLoaded = false;

        if (this.isItem(something)) {
            var children = this.getValue(something, "children");
            if (children === true) {
                // need to lazy loading children
                isLoaded = false;
            } else {
                isLoaded = true;
            }
        }

        return isLoaded;
    },

    /* @Override */
    loadItem: function(/* object */ args) {
        if (this.isItemLoaded(args.item)) {
            return;
        }

        var item = args.item;
        var scope = args.scope || dojo.global;
        var sort = args.sort || null;
        var onItem = args.onItem;
        var onError = args.onError;

        if (dojo.isArray(item)) {
            item = item[0];
        }

        // load children
        var children = this.getValue(item, "children");

        // load children
        if (children === true) {
            var serverQuery = {};

            // "parent" param
            var itemId = this.getValue(item, "id");
            serverQuery["parent"] = itemId;

            // "sort" param
            if (sort) {
                var attribute = sort.attribute;
                var descending = sort.descending;
                serverQuery["sort"] = (descending ? "-" : "") + attribute;
            }

            // ajax request
            var _self = this;

            var xhrData = {
                url: this.url,
                handleAs: "json",
                content: serverQuery
            };

            var xhrFunc = (this.requestMethod.toLowerCase() === "post") ? dojo.xhrPost : dojo.xhrGet;
            var deferred = xhrFunc(xhrData);

            // onError callback
            deferred.addErrback(function(error) {
                if (args.onError) {
                    args.onError.call(scope, error);
                }
            });

            // onLoad callback
            deferred.addCallback(function(data) {
                if (!data) {
                    return;
                }

                if (dojo.isArray(data)) {
                    var children = data;

                    var parentItemId = itemId;
                    var childItems = [];

                    dojo.forEach(children, function(childData) {
                        // build child item
                        var childItem = {};
                        childItem.i = childData;
                        childItem.r = this;

                        childItems.push(childItem);
                    }, _self);

                    _self.setValue(item, "children", childItems);
                }

                if (args.onItem) {
                    args.onItem.call(scope, item);
                }
            });
        }
    },

    /* @Override */
    getValues: function(item, attribute) {
        //  summary:
        //      See dojo.data.api.Read.getValues()

        this._assertIsItem(item);
        if (this.hasAttribute(item, attribute)) {
            return item.i[attribute] || [];
        }

        return []; // Array
    },

    /* @Override */
    setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value) {
        // summary: See dojo.data.api.Write.set()

        // Check for valid arguments
        this._assertIsItem(item);
        this._assert(dojo.isString(attribute));
        this._assert(typeof value !== "undefined");

        var success = false;
        var _item = item.i;
        _item[attribute] = value;
        success = true;

        return success; // boolean
    },

    /* @Override */
    _assert: function(/* boolean */ condition) {
        if (!condition) {
            throw new Error("assertion failed in CustomQueryStore");
        }
    }
});